﻿/*
 * Copyright © 2025−2026 WEI.ZHOU (Willis) (Chengdu). All rights reserved.
 *
 * THIS SOURCE CODE IS PROPRIETARY AND CONFIDENTIAL.
 * It is the exclusive property of WEI.ZHOU (Willis) and may not be
 * used, copied, distributed, or disclosed outside of WEI.ZHOU (Willis)
 * without express written permission.
 *
 * This code is provided to WEI.ZHOU (Willis) under a limited,
 * non-transferable internal license, and is for use only with the author's
 * approved purposes. No external license is granted or implied.
 *
 * Any unauthorized use, reproduction, or distribution is strictly prohibited
 * and may result in severe civil and criminal penalties.
 */

namespace Aore.Utility.IdGenerator.SnowflakeId;

public sealed class SnowflakeIdGenerator
{

  // 例子参数，请根据你环境改为合适的值
  private const long Twepoch = 1288834974657L; // 自定义的自增时间起点
  private const int WorkerIdBits = 5; // 支持最多 32 个节点
  private const int SeqBits = 12; // 序列号位数
  private const long MaxWorkerId = -1L ^ -1L << WorkerIdBits;
  private static readonly object _syncRoot = new();

  private readonly long _workerId;
  private long _lastTimestamp = -1L;
  private long _sequence;

  public SnowflakeIdGenerator(long workerId)
  {
    if (workerId < 0 || workerId > MaxWorkerId) throw new ArgumentOutOfRangeException(nameof(workerId));
    _workerId = workerId;
  }

  public long NextId()
  {
    lock (_syncRoot)
    {
      long timestamp = CurrentTimeMillis();
      if (timestamp == _lastTimestamp)
      {
        _sequence = _sequence + 1 & (1L << SeqBits) - 1;
        if (_sequence == 0)
        {
          // 序列溢出，等待下一个毫秒
          timestamp = TillNextMillis(_lastTimestamp);
        }
      }
      else
      {
        _sequence = 0;
      }

      _lastTimestamp = timestamp;

      long id = timestamp - Twepoch << WorkerIdBits + SeqBits
                | _workerId << SeqBits
                | _sequence;
      return id;
    }
  }

  private long TillNextMillis(long lastTimestamp)
  {
    long timestamp = CurrentTimeMillis();
    while (timestamp <= lastTimestamp)
    {
      timestamp = CurrentTimeMillis();
    }
    return timestamp;
  }

  private long CurrentTimeMillis()
  {
    return DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  }
}